import { _decorator, Component, RigidBody2D, Collider2D, find, Contact2DType, resources, IPhysics2DContact, EventTarget, Sprite, SpriteFrame, math } from 'cc'
import { GameManage } from './GameManage'
import { State } from './State'
const { ccclass, property } = _decorator
const eventTarget = new EventTarget()
@ccclass('Fruits')
export class Fruits extends Component {
  private rBody2D: RigidBody2D = null // 刚体组件
  private collider: Collider2D = null // 碰撞器组件
  fruitsType: number = 1 // 水果类型
  spriteFrame: SpriteFrame = null // 水果图片
  isDown: boolean = true // 是否需要下拉掉落
  collisionSwitch: boolean = true // 本节点碰撞开关
  gameManageScript: GameManage // 游戏节点脚本
  automaticDown: boolean = false // 是否自动下落
  time = 0 // 当前接触时间
  gameOverTime = 5 // 接触最大时间 结束游戏
  isTime = false // 是否正在计时

  // 水果放大比例
  fruitsScaleData = {
    1: 1,
    2: 2,
    3: 2.5,
    4: 3,
    5: 3.5,
    6: 4,
    7: 4.5,
    8: 5,
    9: 5.5,
    10: 6,
    11: 6.5,
  }

  start() {
    this.collider = this.node.getComponent(Collider2D) // 获取碰撞器组件
    this.rBody2D = this.node.getComponent(RigidBody2D) // 获取刚体组件

    // 关闭物理效果
    if (this.isDown) this.rBody2D.enabled = false

    // 获取并设置水果图片
    this.node.getComponent(Sprite).spriteFrame = this.spriteFrame

    // 设置水果缩放体积
    this.setScale(this.fruitsScaleData[this.fruitsType])

    // 添加标记 便于分辨水果类型
    this.node.name = this.node.name + '-' + this.fruitsType

    // 监听开始碰撞
    this.collider.on(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this)

    // 监听结束碰撞
    this.collider.on(Contact2DType.END_CONTACT, this.onEndContact, this)

    // 设置自动下落
    if (this.automaticDown) this.down(math.randomRangeInt(30, 600))
  }

  onDisable() {
    // 销毁监听开始碰撞
    this.collider.off(Contact2DType.BEGIN_CONTACT, this.onBeginContact, this)
  }

  /*
    水果缩放效果
    scale 目标缩放大小
  */
  setScale(scale: number): void {
    const step = 20
    const speed = scale / step
    let currentScale = 1
    this.schedule(
      () => {
        let scale = currentScale * speed
        this.node.setScale(scale, scale)
        currentScale++
        if (currentScale === step) {
          if (this.isDown) {
            this.gameManageScript?.setTouchIsDown(true)
          }
        }
      },
      0.001,
      step - 1,
    )
  }

  /* 
    移动水果 
    XPos x轴位置
  */
  down(XPos: number): void {
    if (!this.isDown) return
    this.node.setPosition(XPos, 1200) // 设置位置
    this.rBody2D.enabled = true // 开启物理效果
  }

  /* 
    监听碰撞 只在两个碰撞体开始接触时被调用一次
    selfCollider：指的是回调脚本的节点上的碰撞体；
    otherCollider：指的是发生碰撞的另一个碰撞体；
    contact：是一个 IPhysics2DContact。包含碰撞最主要的信息。其中比较常用的信息就是碰撞的位置和法向量，contact 内部是按照刚体的本地坐标来存储信息的，而我们一般需要的是世界坐标系下的信息，我们可以通过 contact.getWorldManifold 来获取这些信息。注意，在 Builtin 物理模块这个参数为空。
  */
  onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
    // 判断碰撞开关
    if (!this.collisionSwitch || !otherCollider.node.getComponent(Fruits)?.collisionSwitch) return

    const otherFruitsName: string = otherCollider.node.name
    // 碰撞红线处理
    if (otherFruitsName === 'checkLine') {
      if (!this.isTime) {
        this.isTime = true
        this.schedule(this.timeCallBack, 1)
      }
    }

    // 碰撞融合处理
    // 1.判断是否是同一个大小的水果或者是否是最大的水果
    const selFruitsType: number = this.fruitsType
    const otherFruitsType: number = parseInt(otherFruitsName.split('-')[1])
    if (selFruitsType !== otherFruitsType || selFruitsType === 11) return

    // 2.判断生成新水果的位置
    const selfPos = this.node.getPosition()
    const otherPos = otherCollider.node.getPosition()
    // 2.1 高度不一 取最低
    if (selfPos.y < otherPos.y) {
      this.fruitFrolic(selFruitsType, selfPos) // 通知游戏节点创建大一级的水果
    }
    // 2.2 高度一致 取最慢
    else if (selfPos.y === otherPos.y) {
      let selfSpeedX = this.rBody2D.linearVelocity.x // 本节点的x速度
      let otherSpeedX = otherCollider.node.getComponent(RigidBody2D).linearVelocity.x // 对撞节点的x速度
      if (selfSpeedX > otherSpeedX) {
        // console.log('高度一致 我慢')
        this.fruitFrolic(selFruitsType, selfPos) // 通知游戏节点创建大一级的水果
      }
      // 2.3 高度一致 速度一致 取最右
      else if (selfSpeedX === otherSpeedX) {
        // console.log('高度一致 速度一致')
        if (selfPos.x > otherPos.x) {
          this.fruitFrolic(selFruitsType, selfPos) // 通知游戏节点创建大一级的水果
          // console.log('高度一致 速度一致 取最右')
        }
      } else if (selfSpeedX < otherSpeedX) {
        // console.log('高度一致 我快')
      }
    }

    // 3.销毁本水果节点 (不加定时器会报错)
    this.scheduleOnce(() => {
      this.node.destroy()
    }, 0.01)
  }

  /* 
  通知游戏节点创建大一级的水果
  selFruitsType 销毁的水果类型
  selfPos 生成大一级水果的位置
  */
  fruitFrolic(selFruitsType, selfPos) {
    this.collisionSwitch = false
    this.gameManageScript?.onFruitFrolic(selFruitsType, selfPos)
  }

  /* 
    监听结束碰撞
    selfCollider：指的是回调脚本的节点上的碰撞体；
    otherCollider：指的是发生碰撞的另一个碰撞体；
    contact：是一个 IPhysics2DContact。包含碰撞最主要的信息。其中比较常用的信息就是碰撞的位置和法向量，contact 内部是按照刚体的本地坐标来存储信息的，而我们一般需要的是世界坐标系下的信息，我们可以通过 contact.getWorldManifold 来获取这些信息。注意，在 Builtin 物理模块这个参数为空。
  */
  onEndContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
    const otherFruitsName: string = otherCollider.node.name
    // 结束碰撞红线处理
    if (otherFruitsName === 'checkLine') {
      this.unschedule(this.timeCallBack)
      this.reactTime()
    }
  }

  // 计时计时器回调
  timeCallBack() {
    this.time++
    if (this.time >= this.gameOverTime) {
      this.reactTime()
      this.gameManageScript?.resetGame() // 重置游戏
      this.unschedule(this.timeCallBack)
    }
  }

  // 重置接触信息
  reactTime() {
    this.time = 0 // 当前接触时间
    this.isTime = false // 是否正在计时
  }

  /* 
    设置数据
    gameManageScript  游戏节点脚本
    fruitsType 水果类型
    spriteFrame 水果图片
    isDown 是否需要下拉掉落
    automaticDown 是否自动下落
  */
  set(gameManageScript: GameManage, spriteFrame: SpriteFrame, fruitsType: number, isDown: boolean, automaticDown: boolean) {
    Object.assign(this, {
      gameManageScript,
      fruitsType,
      spriteFrame,
      isDown,
      automaticDown,
    })
  }
}
